Docker on FreeBSD
https://qml.610t.org/squeak/kbug.files/logo-full.png
https://www.docker.com/sites/default/files/d8/styles/role_icon/public/2019-07/Moby-logo.png?itok=sYH_JEaJ
はじめに
世間では、Linuxのコンテナ型仮想化システムであるDockerが大流行りです。
ですが、僕たちFreeBSDユーザは、Linuxで動くものはウチでも動くの精神で、色々な技術を持っています。
で、DockerをFreeBSDで動かす方法にはどんなのがあるのか、調べてみました。
むとうたけしって?
所属コミュニティ
むとうの事情
専業主夫だし、家事終わったら時間もあるし、なんかしよ。
で、Docker on FreeBSDってあるらしい。
今どうなってんの?
どうしたら、FreeBSDで、Docker使えそう?
という感じの話題提供になります。
Dockerってなんぞや?
Dockerは、Linuxで利用できるコンテナ型仮想化技術です。 Linux用のコンテナ仮想化技術ですが、ホストOSで仮想マシン(VM)を利用してLinux kernelを動かすことで、macOS(HyperKit)やWindows(Hyper-V)でも利用可能です。
Dockerでは、単にコンテナ型仮想化を実現するだけではなく、作成したコンテナイメージを共有したり、そのイメージを元に新たなイメージを作成したりが簡単にできるようになっています。
コンテナイメージの作成には、Dockerfileというファイルを利用しますが、このファイルはサーバなどの構築指示書となるため、共同作業者間でサーバに関する情報の共有をするためにも役立ちます。
なぜ、FreeBSDのコンテナ型仮想化技術であるjailは先にできていたのに負けたのかを少し考えてみます。 (FreeBSDワークショップの第69回 Slackコメントとむとうの意見より) ツールとしての使いやすさ(jail pullとかあれば…)
エコシステムの完成
Docker hubでのコンテナイメージ共有とそのイメージの流用の容易性
デファクトスタンダードになったことの優位性
FreeBSDでDockerを動かせる(可能性のある)方法
以下のような方法で、FreeBSDでDockerが動かせる可能性があります。
順番は、難易度の低いものから、高いものの順です。
VirtualBoxなどの仮想マシンを動かして、その上にDocker対応のLinuxを動かす。
ほぼ当たり前に動く。
手元のマシンでは、VT-XやEPTがまともに動くものがなかったため、試すことができませんでしたorz
仮想環境bhyveの上にDocker対応のLinuxを動かして、Linux用Dockerを動かす。
bhyveは、BSD用の仮想マシン型の仮想化技術です。
Kubernetesも動くらしい。
手元のマシンでは、VT-XやEPTがまともに動くものがなかったため、試すことができませんでしたorz
FreeBSD jail内で、Linuxエミュレータ(Linuxlator)を経由して、その中でDockerを動かす。
10Rや11Rの頃は、一時期普通に動いていたらしい。
現在は壊れているが後述のパッチで動作可能。
DockerをFreeBSDでビルドして動かす。
Dockerで公式に提供されているコンテナイメージはLinux用なので、それらが利用できない。でもLinuxlatorが頑張れば動くかも?
コンテナ型仮想化FreeBSD jailってなに?
コンテナ型仮想化とは、基本的には、名前空間の分離(chroot+α)のための技術です。
名前空間でいう名前とは、OSの資源を区別するためにつけられるラベルです。
古くから、chrootというファイルシステムの名前空間を分離する技術があります。
これは、ホスト側のOSの特定のディレクトリを、あたかもrootファイルシステムのように見せかける技術です。
chrootを使うことで、ホストOSとはファイル空間を分離した上でOSを動作可能となるため、実験環境としてよく用いられます。
コンテナ型仮想化技術FreeBSD jailでは、このようなファイルシステムの名前空間の分離以外に、以下のような資源も名前空間の分離が行われます。
Filesystem (chroot)
kernel名前空間
sysctl MIB
proc list
Process ID空間
ネットワーク(IPアドレスなど)
ユーザ名(UID)
FreeBSD jailに関しては、以下のような文章や動画がありますので、参考にしてください。
著者 川口 直也
判型 B5変型、336頁
ISBN 978-4-87783-478-4
価格 本体 3,000円(税込 3,300円)
発行日 2020年12月10日(初版 第1刷発行)
LinuxエミュレータLinuxlatorってなに?
Linuxのシステムコールを、FreeBSDのシステムコールに置き換えることで実現しています。
カーネルモジュールlinux.koで実現されていますが、kernelの再構築で入れることも可能です。
Linuxlatorを利用するためには、/etc/rc.confや/etc/fstabなどに以下のような設定が必要です。
code:/etc/rc.conf
linux_enable="YES"
code:/etc/fstab
# Linuxlator
linprocfs /compat/linux/proc linprocfs rw 0 0
linsysfs /compat/linux/sys linsysfs rw 0 0
devfs /compat/linux/dev devfs rw 0 0
tmpfs /compat/linux/dev/shm tmpfs rw,mode=1777 0 0
/etc/fstabでマウントするディレクトリを作成しておきます。
code:shell
% sudo mkdir -p /compat/linux/sys
% sudo mkdir -p /compat/linux/dev/shm
Linuxユーザランド環境は、portsの/usr/ports/emulators/linux*にあり、pkgでインストールすることも可能です。
code:shell
% sudo pkg install linux_base-c7
% sudo pkg install linux-c7
ライブラリが足りない場合は、rpmなどを取って来て、手動でインストールしてやることで、動く場合もあります。
例えば、以下のような手順になります。
足りないライブラリを確認を確認します。
code:shell
% ldd /compat/linux/usr/bin/digilent-agent |grep found
libQt5Widgets.so.5 => not found
libQt5Gui.so.5 => not found
libQt5Network.so.5 => not found
libQt5SerialPort.so.5 => not found
libQt5Core.so.5 => not found
順次、足りないライブラリを入れながら、lddで確認を繰り返します。
code:shell
% cd /compat/linux
% rpm2cpio < ~/Downloads/qt5-qtbase-5.9.2-3.el7.x86_64.rpm|sudo cpio -id
% rpm2cpio < ~/Downloads/qt5-qtserialport-5.9.2-1.el7.x86_64.rpm |sudo cpio -id
︙
Docker関連ports/pkg
最近のFreeBSDでは、以下のようなDocker関連のports/pkgがあります。
code:shell
% ls -ld /usr/ports/sysutils/docker*
drwxr-xr-x 3 root wheel 512 Jan 24 2020 /usr/ports/sysutils/docker
drwxr-xr-x 3 root wheel 512 Oct 23 09:02 /usr/ports/sysutils/docker-compose
drwxr-xr-x 2 root wheel 512 Jan 24 2020 /usr/ports/sysutils/docker-credential-pass
drwxr-xr-x 3 root wheel 512 Dec 24 2019 /usr/ports/sysutils/docker-freebsd
drwxr-xr-x 3 root wheel 512 Oct 23 09:02 /usr/ports/sysutils/docker-machine
drwxr-xr-x 3 root wheel 512 May 16 14:07 /usr/ports/sysutils/docker-registry
各portsの説明は、以下のようになっています。
code:shell
% grep COMMENT /usr/ports/sysutils/docker*/Makefile
docker-compose/Makefile: COMMENT=Define and run multi-container applications with Docker
docker-credential-pass/Makefile:COMMENT=Helper to use sysutils/password-store as Docker credentials store
docker-freebsd/Makefile: COMMENT=Docker containment system
docker-machine/Makefile: COMMENT=Tool to create Docker hosts
docker-registry/Makefile: COMMENT=Docker implementation of the OCI Distribution Specification
docker/Makefile: COMMENT=Open-source application container engine
Docker関連pkgを全てインストールするには、例えば以下のようにします。
code:shell
% ls -ld /usr/ports/sysutils/docker*|awk '{print "pkg install -y "$9}'|sudo sh
このうち、sysutils/dockerはdockerコマンドで、Dockerの操作に使われるクライアントになります。
Dockerエンジンは、sysutils/docker-freebsdになりますが、現在はBROKENとなって削除されており、構築できません。
以降、Dockerエンジンを動かす方法がないかを探っていきます。
Dockerエンジン portsの現在/過去/未来
Dockerエンジンportsには、本家のsysutils/docker-freebsd以外にも、いくつか異なるリポジトリで提供されている作業版があります。
lang/goが更新される前に最後に動いていた動作版は2019/01/14のRev 49308 現在は、BROKENのまま削除済
最終更新: 2015/07/01
最終更新: 2020/10/16
以下の二つのバージョンを提供
sysutils/docker-freebsd:
sysutils/docker-engine: ベース版 Dockerエンジンv19.03.13を動かす
作業開始: 2019/09/09
最新コメント: 2020/11/08
mobyの開発が活発(最新更新:2020/12/01)で、既に壊れている
最終コメント: 2020/12/18
kvasdopil版ベースで、19.03.13でコンパイルできるようにしたもの
以上の情報を時系列順でまとめると、表のようになります。
table:Dockerエンジンのまとめ
仮称 Dockerエンジンバージョン 最終更新など ビルド状況 動作状況 動作OS 備考
kvasdopil版 1.7.0-dev 2015/07/01 x x 本家版の元になっているもの?
本家SVN最終版 1.7.0-dev 2019/01/14 o (patch) △ ubuntu, debian kinoshita版patchで構築可能。centos:latestは動かない
decke版(docker-freebsd) 17.05.0-ce 2019/03/15 x x kinoshita版patchでは不足あり
decke版(docker-engine) 19.03.13 2020/10/24 x x dockerdが構築できず
review版 19.03.13 2020/11/08 ? ? むとう検証できず
moby 19.03.13 2020/12/01 x x 開発は活発で、FreeBSD用の作業も含まれている
kinoshita版(kvasdopil版ベース) 19.03.13 2020/12/18 o (patch) x Dockerエンジンは動くがrunできない
FreeBSDワークショップでのkinoshitaさんの作業:最低限のpatchでのビルド
はじめに、以下のようにして、kvasdopil版のfreebsd-compatブランチを取得します。
code:shell
% cd docker
% git checkout freebsd-compat
パッチは、以下の通りです。
lang/goのバージョンが上がり、syscall.Mknod(path, mode, dev)のdev引数の型がintからunit64に変わったことが、コンパイルができなくなった要因のようです。
code:diff
diff -ruN docker/VERSION docker-kinosita/VERSION
--- docker/VERSION 2020-12-18 14:02:27.272487000 +0900
+++ docker-kinosita/VERSION 2020-12-18 14:03:17.712803000 +0900
@@ -1 +1 @@
-1.7.0-dev
+19.03.13
diff -ruN docker/pkg/archive/archive.go docker-kinosita/pkg/archive/archive.go
--- docker/pkg/archive/archive.go 2020-12-18 14:02:27.888712000 +0900
+++ docker-kinosita/pkg/archive/archive.go 2020-12-18 14:04:12.884245000 +0900
@@ -301,7 +301,8 @@
mode |= syscall.S_IFIFO
}
- if err := system.Mknod(path, mode, int(system.Mkdev(hdr.Devmajor, hdr.Devminor))); err != nil {
+ if err := system.Mknod(path, mode, uint64(system.Mkdev(hdr.Devmajor, hdr.Devminor))); err != nil {
+
return err
}
diff -ruN docker/pkg/system/mknod.go docker-kinosita/pkg/system/mknod.go
--- docker/pkg/system/mknod.go 2020-12-18 13:55:55.812538000 +0900
+++ docker-kinosita/pkg/system/mknod.go 2020-12-18 14:03:46.492720000 +0900
@@ -8,7 +8,7 @@
// Mknod creates a filesystem node (file, device special file or named pipe) named path
// with attributes specified by mode and dev
-func Mknod(path string, mode uint32, dev int) error {
+func Mknod(path string, mode uint32, dev uint64) error {
return syscall.Mknod(path, mode, dev)
}
ビルドは、以下のように正常に終わります。
code:shell
% cd docker-kinosita
% export AUTO_GOPATH=1
% ./hack/make.sh binary
# WARNING! I don't seem to be running in the Docker container.
# The result of this command might be an incorrect build, and will not be
# officially supported.
#
# Try this instead: make all
#
---> Making bundle: binary (in bundles/19.03.13/binary)
Building: /home/mutoh/work/docker/github/docker-kinosita/bundles/19.03.13/binary/docker-19.03.13
Created binary: /home/mutoh/work/docker/github/docker-kinosita/bundles/19.03.13/binary/docker-19.03.13
ZFSでdocker用の作業用領域を作成し、Dockerエンジンを起動します。
code:shell
% sudo zfs create -o mountpoint=/docker zroot/docker
% sudo ./bundles/latest/binary/docker -d -e jail -s zfs -g /docker -D
バージョンは正しく取得できます。
code:shell
% ./bundles/latest/binary/docker version
Client version: 19.03.13
Client API version: 1.19
Go version (client): go1.15.6
Git commit (client): b1c37d233-dirty
OS/Arch (client): freebsd/amd64
Server version: 19.03.13
Server API version: 1.19
Go version (server): go1.15.6
Git commit (server): b1c37d233-dirty
OS/Arch (server): freebsd/amd64
また、Dockerのイメージの取得も可能です。
code:shell
% ./bundles/latest/binary/docker pull hello-world
latest: Pulling from hello-world
482c215f2373: Pull complete
fd74c3615f76: Pull complete
Digest: sha256:0ebe6f409b373c8baf39879fccee6cae5e718003ec3167ded7d54cb2b5da2946
Status: Downloaded newer image for hello-world:latest
しかし、Dockerイメージの実行はできません。
code:shell
% ./bundles/latest/binary/docker run hello-world
Error response from daemon: json: cannot set embedded pointer to unexported struct: runconfig.hostConfigWrapper
この時、Dockerエンジン側では、以下のようなエラーメッセージが出力されています。
code:shell
% sudo ./bundles/latest/binary/docker -d -e jail -s zfs -g /docker -D
(snip)
DEBU0052 Calling POST /containers/create INFO0052 POST /v1.19/containers/create? ERRO0052 Handler for POST /containers/create returned error: json: cannot set embedded pointer to unexported struct: runconfig.hostConfigWrapper ERRO0052 HTTP Error err=json: cannot set embedded pointer to unexported struct: runconfig.hostConfigWrapper statusCode=500 とりあえず、現状では、これ以上の原因追求はできていません。
本家SVN最終版でDockerエンジンを動かす
このバージョンのDockerエンジンは、1.7.0-devと相当古いものになっています。
動作が可能であった最新バージョンである490308のportsを取得するためには、以下のようにします。
code:shell
A docker-freebsd/files
A docker-freebsd/Makefile
A docker-freebsd/distinfo
A docker-freebsd/files/docker.in
A docker-freebsd/files/patch-pkg_system_meminfo__unsupported.go
A docker-freebsd/files/patch-runconfig_config.go
A docker-freebsd/files/patch-runconfig_hostconfig.go
A docker-freebsd/pkg-descr
A docker-freebsd/pkg-message
Checked out revision 490308.
この時点のsysutils/docker-freebsdは、既にBROKENのためそのままでは構築することはできません。
しかし、kinoshita版パッチに相当するものを当てることで、動作する状態まで持っていけます。
code:diff
--- pkg/archive/archive.go.orig 2020-12-22 07:46:00 UTC
+++ pkg/archive/archive.go
@@ -301,7 +301,7 @@ func createTarFile(path, extractDir string, hdr *tar.H
mode |= syscall.S_IFIFO
}
- if err := system.Mknod(path, mode, int(system.Mkdev(hdr.Devmajor, hdr.Devminor))); err != nil {
+ if err := system.Mknod(path, mode, uint64(system.Mkdev(hdr.Devmajor, hdr.Devminor))); err != nil {
return err
}
--- pkg/system/mknod.go.orig 2015-06-08 13:34:30 UTC
+++ pkg/system/mknod.go
@@ -8,7 +8,7 @@ import (
// Mknod creates a filesystem node (file, device special file or named pipe) named path
// with attributes specified by mode and dev
-func Mknod(path string, mode uint32, dev int) error {
+func Mknod(path string, mode uint32, dev uint64) error {
return syscall.Mknod(path, mode, dev)
}
インストールは、以下のように行います。
code:shell
$ cd docker-freebsd/
$ sudo make install
(snip)
Dockerエンジンを実行するための設定を/etc/rc.confに行います。
code:/etc/rc.conf
# Docker
docker_enable="YES"
docker_dir=/docker
Dockerエンジンの作業用ディレクトリをZFS上に構築します。
code:shell
% sudo zfs create -o mountpoint=/docker zroot/docker
以下のように、Dockerエンジンを起動します。
code:shell
% sudo service docker start
バージョンは、以下のように取得できています。
code:shell
$ docker version
Client version: 1.7.0-dev
Client API version: 1.19
Go version (client): go1.15.6
Git commit (client): 582db78
OS/Arch (client): freebsd/amd64
Server version: 1.7.0-dev
Server API version: 1.19
Go version (server): go1.15.6
Git commit (server): 582db78
OS/Arch (server): freebsd/amd64
以下のように、Dockerコンテナイメージも取得できています。
code:shell
$ docker pull hello-world
latest: Pulling from hello-world
482c215f2373: Pull complete
fd74c3615f76: Pull complete
Digest: sha256:0ebe6f409b373c8baf39879fccee6cae5e718003ec3167ded7d54cb2b5da2946
Status: Downloaded newer image for hello-world:latest
hello-world Dockerコンテナイメージは、以下のように実行可能です。
code:shell
$ docker run hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
For more examples and ideas, visit:
Ubunt Dockerコンテナイメージは、以下のように動作します。
code:shell
$ docker run ubuntu
Unable to find image 'ubuntu:latest' locally
latest: Pulling from ubuntu
1a8350c8f1ab: Pull complete
833936205d34: Pull complete
cefc2c955982: Pull complete
d6a22aa06b3a: Pull complete
7d5ba4fdfd37: Pull complete
Digest: sha256:0f077e58a161913d8d0804eebb2ea432e5322d90555a000d92b3df849ca26f5c
Status: Downloaded newer image for ubuntu:latest
$ docker run -it ubuntu bash
root@:/# ps
PID TTY TIME CMD
1084 pts/2 00:00:00 ps
1055 pts/2 00:00:00 bash
同様にして、centos Dockerコンテナイメージを実行しても、エラーになってしまいます。
code:shell
$ docker run centos
Unable to find image 'centos:latest' locally
latest: Pulling from centos
9bd570a5383f: Extracting 8.913 MB/75.18 MB
1f260b789676: Download complete
49387ccacc22: Download complete
Pulling repository centos
Could not reach any registry endpoint
この時のDockerエンジンのエラーメッセージは、以下の通りです。
code:shell
$ sudo /usr/local/bin/docker -d -e jail -s zfs -g /docker -D
(snip)
DEBU0055 zfs zfs destroy -r zroot/docker/9bd570a5383f3a7ddf7b5314cece481aec259cf45ae14913ce2c530442d5a3bc DEBU0055 zfs zfs create -o mountpoint=legacy zroot/docker/9bd570a5383f3a7ddf7b5314cece481aec259cf45ae14913ce2c530442d5a3bc DEBU0055 zfs zfs get -Hp all zroot/docker/9bd570a5383f3a7ddf7b5314cece481aec259cf45ae14913ce2c530442d5a3bc DEBU0055 zfs mount("zroot/docker/9bd570a5383f3a7ddf7b5314cece481aec259cf45ae14913ce2c530442d5a3bc", "/docker/zfs/graph/9bd570a5383f", "") DEBU0055 Start untar layer DEBU0056 zfs unmount("/docker/zfs/graph/9bd570a5383f") DEBU0056 zfs zfs destroy -r zroot/docker/9bd570a5383f3a7ddf7b5314cece481aec259cf45ae14913ce2c530442d5a3bc ERRO0056 Error from V2 registry: ApplyLayer exit status 1 stdout: stderr: platform and architecture is not supported DEBU0056 image does not exist on v2 registry, falling back to v1 DEBU0056 pulling v1 repository with local name "centos" DEBU0056 hostDir: /etc/docker/certs.d/index.docker.io DEBU0057 Retrieving the tag list DEBU0057 hostDir: /etc/docker/certs.d/registry-1.docker.io ERRO0058 unable to get remote tags: Could not reach any registry endpoint こちらも、これ以上の原因追求はできていません。
decke版docker-engine
code:shell
% cd ports/sysutils/docker-engine/
% sudo pkg install go git sqlite3 bash ca_root_nss
% make
===> License APACHE20 accepted by the user
===> docker-engine-19.03.13 depends on file: /usr/local/sbin/pkg - found
===> Fetching all distfiles required by docker-engine-19.03.13 for building
===> Extracting for docker-engine-19.03.13
=> SHA256 Checksum OK for moby-moby-v19.03.13_GH0.tar.gz.
===> Patching for docker-engine-19.03.13
===> Applying FreeBSD patches for docker-engine-19.03.13 from /usr/home/mutoh/work/docker/ports/ports/sysutils/docker-engine/files
===> docker-engine-19.03.13 depends on executable: bash - found
===> docker-engine-19.03.13 depends on file: /usr/local/bin/go - found
===> Configuring for docker-engine-19.03.13
===> Building for docker-engine-19.03.13
Removing bundles/
---> Making bundle: binary (in bundles/binary)
Building: bundles/binary-daemon/dockerd-dev
GOOS="" GOARCH="" GOARM=""
Created binary: bundles/binary-daemon/dockerd-dev
===> Staging for docker-engine-19.03.13
===> Generating temporary packing list
for t in ./cmd/dockerd; do dst=$(echo ${t} | /usr/bin/sed -Ee 's/^^:*:(^:+).*$/\1/' -e 's/^\.$/docker-engine/'); src=$(/usr/bin/basename ${dst}); case ${dst} in /*) dst=/usr/home/mutoh/work/docker/ports/ports/sysutils/docker-engine/work/stage${dst}; /bin/mkdir -p $(/usr/bin/dirname ${dst}) ;; *) dst=/usr/home/mutoh/work/docker/ports/ports/sysutils/docker-engine/work/stage/usr/local/bin/${src} ;; esac; echo "===> Installing ${src} as ${dst}"; install -s -m 555 /usr/home/mutoh/work/docker/ports/ports/sysutils/docker-engine/work/bin/${src} ${dst}; done ===> Installing dockerd as /usr/home/mutoh/work/docker/ports/ports/sysutils/docker-engine/work/stage/usr/local/bin/dockerd
install: /usr/home/mutoh/work/docker/ports/ports/sysutils/docker-engine/work/bin/dockerd: No such file or directory
*** Error code 71
Stop.
make: stopped in /usr/home/mutoh/work/docker/ports/ports/sysutils/docker-engine
review版
これは、mobyの開発が活発なことに起因しているようです。
かなりのpatchがupstreamに取り入れられており、必要が無くなっています。
おわりに
いかがでしたか?
今回は、最新のDocker環境をまともにFreeBSD jail+LinuxlatorでDockerを動かすところまで、たどり着けませんでした、すみません。
ただ、古いバージョンである1.7.0-devでのDockerはそれなりに動いているようです。
現状での情報はできる限り集めたつもりですので、参考になれば幸いです。
今度は、あなたが完全に動かして、情報公開していただければ、うれしいです。
参考文献
Phabricator
send-pr